package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/apache/rocketmq-client-go/v2/consumer"
	"go.uber.org/zap"
	"time"

	"github.com/apache/rocketmq-client-go/v2"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	"github.com/apache/rocketmq-client-go/v2/producer"
	"golang.org/x/exp/rand"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"

	"mxshop_srvs/order_srv/global"
	"mxshop_srvs/order_srv/model"
	"mxshop_srvs/order_srv/proto"
)

type OrderServer struct {
}

func GenerateOrderSn(userId int32) string {
	//订单号的生成规则
	/*
		年月日时分秒+用户id+2位随机数
	*/
	now := time.Now()
	rand.Seed(uint64(time.Now().UnixNano()))
	orderSn := fmt.Sprintf("%d%d%d%d%d%d%d%d",
		now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Nanosecond(),
		userId, rand.Intn(90)+10,
	)
	return orderSn
}

// 购物车
// 购物车列表
func (s *OrderServer) CartItemList(_ context.Context, req *proto.UserInfo) (*proto.CartItemListResponse, error) {
	var shopCarts []model.ShoppingCart
	result := global.DB.Where(&model.ShoppingCart{User: req.Id}).Find(&shopCarts)
	if result.RowsAffected == 0 {
		return nil, result.Error
	}
	rsp := proto.CartItemListResponse{
		Total: int32(result.RowsAffected),
	}
	for _, shopCart := range shopCarts {
		rsp.Data = append(rsp.Data, &proto.ShopCartInfoResponse{
			Id:      shopCart.ID,
			UserId:  shopCart.User,
			GoodsId: shopCart.Goods,
			Nums:    shopCart.Nums,
			Checked: shopCart.Checked,
		})
	}
	return &rsp, nil
}

// 购物车添加
func (s *OrderServer) CreateCartItem(_ context.Context, req *proto.CartItemRequest) (*proto.ShopCartInfoResponse, error) {
	// 先查询是否存在该商品
	var shopCart model.ShoppingCart
	result := global.DB.Where(&model.ShoppingCart{Goods: req.GoodsId, User: req.UserId}).First(&shopCart)
	if result.RowsAffected == 0 {
		// 不存在，则新增
		shopCart.User = req.UserId
		shopCart.Goods = req.GoodsId
		shopCart.Nums = req.Nums
		shopCart.Checked = false
	} else {
		// 存在，则更新
		shopCart.Nums += req.Nums
	}
	global.DB.Save(&shopCart)
	return &proto.ShopCartInfoResponse{
		Id: shopCart.ID,
	}, nil
}

// 购物车更新
func (s *OrderServer) UpdateCartItem(_ context.Context, req *proto.CartItemRequest) (*emptypb.Empty, error) {
	var shopCart model.ShoppingCart
	if result := global.DB.Where("goods=? and user=?", req.GoodsId, req.UserId).First(&shopCart); result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "购物车记录不存在")
	}
	if req.Nums > 0 {
		shopCart.Nums = req.Nums
	}
	shopCart.Checked = req.Checked
	global.DB.Save(&shopCart)
	return &emptypb.Empty{}, nil
}

// 购物车删除
func (s *OrderServer) DeleteCartItem(_ context.Context, req *proto.CartItemRequest) (*emptypb.Empty, error) {
	if result := global.DB.Where("goods=? and user=?", req.GoodsId, req.UserId).Delete(&model.ShoppingCart{}); result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "购物车记录不存在")
	}
	return &emptypb.Empty{}, nil
}

type OrderListener struct {
	Code        codes.Code
	Detail      string
	ID          int32
	OrderAmount float32
}

// 本地事务执行
func (o *OrderListener) ExecuteLocalTransaction(msg *primitive.Message) primitive.LocalTransactionState {

	var orderInfo model.OrderInfo
	_ = json.Unmarshal(msg.Body, &orderInfo)

	var goodsIds []int32 // 选中的商品id
	var ShopCarts []model.ShoppingCart
	var goodNumsMap = make(map[int32]int32) // 商品id和数量的映射
	if result := global.DB.Where(&model.ShoppingCart{User: orderInfo.User, Checked: true}).Find(&ShopCarts); result.RowsAffected == 0 {
		o.Code = codes.InvalidArgument
		o.Detail = "没有选中结算的商品"
		return primitive.RollbackMessageState
	}
	for _, shopCart := range ShopCarts {
		goodsIds = append(goodsIds, shopCart.Goods)
		goodNumsMap[shopCart.Goods] = shopCart.Nums
	}
	// 跨服务的调用 商品微服务
	goods, err := global.GoodsSrvClient.BatchGetGoods(context.Background(), &proto.BatchGoodsIdInfo{Id: goodsIds})
	if err != nil {
		o.Code = codes.Internal
		o.Detail = "批量查询商品信息失败"
		return primitive.RollbackMessageState
	}
	var orderAmount float32
	var orderGoods []*model.OrderGoods
	var goodsInvInfo []*proto.GoodsInvInfo
	for _, good := range goods.Data {
		orderAmount += good.ShopPrice * float32(goodNumsMap[good.Id])
		orderGoods = append(orderGoods, &model.OrderGoods{
			Goods:      good.Id,
			GoodsName:  good.Name,
			GoodsImg:   good.GoodFrontImage,
			GoodsPrice: good.ShopPrice,
			Nums:       goodNumsMap[good.Id],
		})
		goodsInvInfo = append(goodsInvInfo, &proto.GoodsInvInfo{
			GoodsId: good.Id,
			Num:     goodNumsMap[good.Id],
		})
	}
	// 库存扣减微服务调用
	if _, err = global.InventorySrvClient.Sell(context.Background(), &proto.SellInfo{
		GoodsInfo: goodsInvInfo,
		OrderSn:   orderInfo.OrderSn,
	}); err != nil {
		// 如果是因为网络问题，如何避免误判，这里暂时不做处理 后续可以增加重试机制，并且进行幂等设计
		o.Code = codes.ResourceExhausted
		o.Detail = "库存扣减失败"
		return primitive.RollbackMessageState
	}

	/*	// 模拟错误
		o.Code = codes.Internal
		o.Detail = "本地事务执行失败"
		return primitive.CommitMessageState*/

	// 生成订单表
	tx := global.DB.Begin()
	orderInfo.OrderMount = orderAmount
	if result := tx.Save(&orderInfo); result.RowsAffected == 0 {
		tx.Rollback()
		o.Code = codes.Internal
		o.Detail = "创建订单失败"
		return primitive.CommitMessageState
	}
	o.OrderAmount = orderAmount
	o.ID = orderInfo.ID
	for _, orderGood := range orderGoods {
		orderGood.Order = orderInfo.ID
	}
	// 批量插入order_goods表
	if result := tx.CreateInBatches(orderGoods, 100); result.RowsAffected == 0 {
		tx.Rollback()
		o.Code = codes.Internal
		o.Detail = "批量插入订单商品失败"
		return primitive.CommitMessageState
	}
	// 删除购物车记录
	if result := tx.Where(&model.ShoppingCart{User: orderInfo.User, Checked: true}).Delete(&model.ShoppingCart{}); result.RowsAffected == 0 {
		tx.Rollback()
		o.Code = codes.Internal
		o.Detail = "删除购物车记录失败"
		return primitive.CommitMessageState
	}

	// 发送延时消息
	p, err := rocketmq.NewProducer(
		producer.WithNameServer([]string{"47.121.177.86:9876"}),
		producer.WithCredentials(primitive.Credentials{
			AccessKey: "mallchat",
			SecretKey: "12345678",
		}),
	)
	if err != nil {
		zap.S().Errorf("创建producer失败: %s", err.Error())
	}
	if err = p.Start(); err != nil {
		zap.S().Errorf("启动producer失败: %s", err.Error())
	}
	msg = primitive.NewMessage("order_timeout", msg.Body)
	msg.WithDelayTimeLevel(16) // 延迟3秒发送
	_, err = p.SendSync(context.Background(), msg)
	if err != nil {
		zap.S().Errorf("发送延时消息失败%v\n", err)
		tx.Rollback()
		o.Code = codes.Internal
		o.Detail = "发送延时消息失败"
		return primitive.CommitMessageState
	}
	/*	if err = p.Shutdown(); err != nil {
		zap.S().Error("关闭producer失败")
	}*/

	tx.Commit()
	o.Code = codes.OK
	return primitive.RollbackMessageState
}

// 进行消息的回查，根据本地事务的状态来决定是否提交或者回滚
func (dl *OrderListener) CheckLocalTransaction(msg *primitive.MessageExt) primitive.LocalTransactionState {
	var orderInfo model.OrderInfo
	_ = json.Unmarshal(msg.Body, &orderInfo)

	//怎么检查之前的逻辑是否完成
	if result := global.DB.Where(model.OrderInfo{OrderSn: orderInfo.OrderSn}).First(&orderInfo); result.RowsAffected == 0 {
		return primitive.CommitMessageState //你并不能说明这里就是库存已经扣减了
	}
	return primitive.RollbackMessageState
}

// 新建订单
func (s *OrderServer) CreateOrder(_ context.Context, req *proto.OrderRequest) (*proto.OrderInfoResponse, error) {
	/**
	1. 从购物车获取选中的商品信息
	2.商品的金额查询，访问商品服务
	3. 库存扣减， 访问库存服务
	4. 订单的基本信息表，订单的商品信息表
	5. 从购物车中删除已购买的记录
	*/
	zap.S().Info("调用CreateOrder")
	orderListener := OrderListener{}
	p, err := rocketmq.NewTransactionProducer(
		&orderListener,
		producer.WithNameServer([]string{"47.121.177.86:9876"}),
		producer.WithCredentials(primitive.Credentials{
			AccessKey: "mallchat",
			SecretKey: "12345678",
		}),
	)
	if err != nil {
		zap.S().Errorf("创建producer失败: %s", err.Error())
		return nil, err
	}
	if err = p.Start(); err != nil {
		zap.S().Errorf("启动producer失败: %s", err.Error())
		return nil, err
	}

	order := model.OrderInfo{
		OrderSn:      GenerateOrderSn(req.UserId),
		Address:      req.Address,
		SignerName:   req.Name,
		SignerMobile: req.Mobile,
		Post:         req.Post,
		User:         req.UserId,
	}
	jsonString, _ := json.Marshal(order)

	_, err = p.SendMessageInTransaction(context.Background(), primitive.NewMessage("order_reback", jsonString))
	if err != nil {
		fmt.Printf("发送事务消息失败: %v", err)
		return nil, status.Error(codes.Internal, "发送消息失败")
	}

	if orderListener.Code != codes.OK {
		return nil, status.Error(codes.Internal, orderListener.Detail)
	}

	return &proto.OrderInfoResponse{
		Id:      orderListener.ID,
		OrderSn: order.OrderSn,
		Total:   orderListener.OrderAmount,
	}, nil
}

// 订单列表
func (s *OrderServer) OrderList(_ context.Context, req *proto.OrderFilterRequest) (*proto.OrderListResponse, error) {
	var orders []model.OrderInfo
	var rsp proto.OrderListResponse
	// where 查询如果是0则会自动忽略
	var total int64
	global.DB.Model(&model.OrderInfo{}).Where(&model.OrderInfo{User: req.UserId}).Count(&total)
	//if result.RowsAffected == 0 {
	//	return nil, status.Error(codes.Internal, "内部错误")
	//}
	rsp.Total = int32(total)
	// 分页查询
	global.DB.Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Where(&model.OrderInfo{User: req.UserId}).Find(&orders)
	for _, order := range orders {
		rsp.Data = append(rsp.Data, &proto.OrderInfoResponse{
			Id:      order.ID,
			UserId:  order.User,
			OrderSn: order.OrderSn,
			PayType: order.PayType,
			Status:  order.Status,
			Post:    order.Post,
			Total:   float32(order.OrderMount),
			Address: order.Address,
			Name:    order.SignerName,
			Mobile:  order.SignerMobile,
			AddTime: order.CreatedAt.Format("2006-01-02 15:04:05"),
		})
	}
	return &rsp, nil
}

// 订单详情
func (s *OrderServer) OrderDetail(_ context.Context, req *proto.OrderRequest) (*proto.OrderInfoDetailResponse, error) {
	var order model.OrderInfo
	var rsp proto.OrderInfoDetailResponse
	result := global.DB.Where(&model.OrderInfo{BaseModel: model.BaseModel{ID: req.Id}, User: req.UserId}).First(&order, req.Id)
	if result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "订单不存在")
	}
	orderInfo := proto.OrderInfoResponse{}
	orderInfo.Id = order.ID
	orderInfo.UserId = order.User
	orderInfo.OrderSn = order.OrderSn
	orderInfo.PayType = order.PayType
	orderInfo.Status = order.Status
	orderInfo.Post = order.Post
	orderInfo.Total = order.OrderMount
	orderInfo.Address = order.Address
	orderInfo.Name = order.SignerName
	orderInfo.Mobile = order.SignerMobile
	rsp.OrderInfo = &orderInfo
	var orderGoods []model.OrderGoods
	global.DB.Where(&model.OrderGoods{Order: order.ID}).Find(&orderGoods)
	for _, goods := range orderGoods {
		rsp.Goods = append(rsp.Goods, &proto.OrderItemResponse{
			GoodsId:    goods.Goods,
			GoodsName:  goods.GoodsName,
			GoodsPrice: goods.GoodsPrice,
			Nums:       goods.Nums,
		})
	}
	return &rsp, nil
}

// 更新订单状态
func (s *OrderServer) UpdateOrderStatus(_ context.Context, req *proto.OrderStatus) (*emptypb.Empty, error) {
	if result := global.DB.Model(&model.OrderInfo{}).Where("order_sn = ?", req.Ordersn).Update("status", req.Status); result.RowsAffected == 0 {
		return nil, status.Error(codes.NotFound, "订单不存在")
	}
	return &emptypb.Empty{}, nil
}

func OrderTimeout(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
	for i := range msgs {
		var orderInfo model.OrderInfo
		_ = json.Unmarshal(msgs[i].Body, &orderInfo)
		fmt.Println("获取到订单超时消息：%v\n", time.Now())
		// 查询订单的支付状态，如果已支付什么都不做，如果未支付，则归还库存
		var order model.OrderInfo
		if result := global.DB.Model(model.OrderInfo{}).Where(model.OrderInfo{OrderSn: orderInfo.OrderSn}).First(&order); result.RowsAffected == 0 {
			return consumer.ConsumeSuccess, nil
		}
		if order.Status != "TRADE_SUCCESS" {
			tx := global.DB.Begin()
			// 归还库存 ， 我们可以模仿order中发送一个消息到order_reback中
			p, err := rocketmq.NewProducer(
				producer.WithNameServer([]string{"47.121.177.86:9876"}),
				producer.WithCredentials(primitive.Credentials{
					AccessKey: "mallchat",
					SecretKey: "12345678",
				}),
			)
			tx.Save(&order)
			if err != nil {
				zap.S().Errorf("创建producer失败: %s", err.Error())
			}
			if err = p.Start(); err != nil {
				zap.S().Errorf("启动producer失败: %s", err.Error())
			}
			_, err = p.SendSync(context.Background(), primitive.NewMessage("order_reback", msgs[1].Body))
			if err != nil {
				tx.Rollback()
				fmt.Println("发送失败：%s\n", err)
				return consumer.ConsumeRetryLater, nil
			}
			/*if err = p.Shutdown(); err != nil {
				panic("关闭producer失败")
			}*/
			order.Status = "TRADE_CLOSE"
		}
	}
	return consumer.ConsumeSuccess, nil
}
